package org.groovyflow.spring.web;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.groovyflow.GroovyFlowContext;
import org.groovyflow.file.DifferenceFinder;
import org.groovyflow.file.FileMapMaker;
import org.groovyflow.file.Traverser;
import org.groovyflow.spring.locator.BeanLocator;
import org.groovyflow.spring.locator.BeanLocatorImpl;

import org.groovyflow.db.SqlDao;

public class GroovyFlowFilter implements Filter {
	
    private BeanLocator beanLocator = new BeanLocatorImpl();
	private Thread t;
	private PartialsBuilder partialsBuilder;
	private SqlDao sqlDao;
	
	public void doFilter(ServletRequest servletRequest, 
			ServletResponse servletResponse, 
			FilterChain chain) 
				throws IOException, ServletException {
		   HttpServletRequest req = (HttpServletRequest) servletRequest;	
		   HttpServletResponse res = (HttpServletResponse) servletResponse;	
		   
		   Chain.reset(req);
		
		   setGroovyFlowContext(req, res);
		   try{
			   chain.doFilter(servletRequest, servletResponse);
		   }finally{
			   unsetGroovyFlowContext();
		   }
	}
	
	protected void setGroovyFlowContext(HttpServletRequest req,
				HttpServletResponse res) {
		Map map = new HashMap();
		
		if(this.partialsBuilder != null){
			PartialsBuilderWrapper pbWrapper = 
				new PartialsBuilderWrapper(this.partialsBuilder, req, res);
			map.put("partials", pbWrapper);
		}
		if(this.sqlDao != null)
			map.put("sqlDao", this.sqlDao);
		
		GroovyFlowContext.setMap(map);		
	}
	
	private void unsetGroovyFlowContext(){
		GroovyFlowContext.unsetMap();
	}

	public void destroy() {
		System.out.println(this.getClass() + " destroy() has been called.");
		if(t != null)
			t.interrupt();
	}

	public void init(FilterConfig arg0) throws ServletException {
		try{
			this.partialsBuilder = (PartialsBuilder) this.beanLocator.getBean("partialsBuilder");
		}catch(org.springframework.beans.factory.NoSuchBeanDefinitionException ex){
			System.out.println("No bean named partialsBuilder was configured.");
		}
		try{
			this.sqlDao = (SqlDao) this.beanLocator.getBean("sqlDao");
		}catch(org.springframework.beans.factory.NoSuchBeanDefinitionException ex){
			System.out.println("No bean named sqlDao was configured.");
		}		
		
        if(this.getScriptManager().getMode().equals("dev")) {
		 	t = new Thread(new Reloader());
			t.setDaemon(true);
			t.start();
        }
	}
	
	public void setBeanLocator(BeanLocator beanLocator) {
		this.beanLocator = beanLocator;
	}	
	
	protected ControllerGroovyScriptEngineManager getScriptManager(){
		return (ControllerGroovyScriptEngineManager) this.beanLocator.getBean("controllerGroovyScriptEngineManager");
	}
	
	protected InterceptorCache getInterceptorCache(){
		return (InterceptorCache) this.beanLocator.getBean("interceptorCache");
	}
	
	private Object lock = new Object();
	private class Reloader implements Runnable{
		Traverser trav;
		Map fileMap;
		public void run() {
			while(true){
				//For now assume there's only one dynamic root
				if(trav == null){
					File root = new File((String) getScriptManager().getDynamicRoots().get(0));
					trav = new Traverser(root);
				}	
				if(fileMap == null){
					FileMapMaker mapMaker = new FileMapMaker();
					trav.setFileHandler(mapMaker);
					trav.traverse();
					this.fileMap = mapMaker.getFileMap();
				}
				else{
					DifferenceFinder diffFinder = new DifferenceFinder();
					diffFinder.setMapFile(fileMap);
					trav.setFileHandler(diffFinder);
					trav.traverse();
					if(diffFinder.isChanged()){
						trav = null;
						fileMap = null;
						getInterceptorCache().clear();
						sqlDao.getTableCache().clear();
						getScriptManager().initGSE();
					}
				}
				
				try {
					Thread.sleep(4000);
				} catch (InterruptedException e) {
				}			
			}
		}
	}		

}
